package main

import (
	"io/ioutil"
	"fmt"
	"bytes"
	"strconv"
	"text/template"
	"os"
)
type Input map[int]struct{}
type Output map[int]struct{}
type Oprand struct{
	Left int
	Right int
}
type Xor map[int]Oprand
type And map[int]Oprand
type Ref map[int]Oprand
type Circuit struct{
	Ins Input
	Out Output
	Xors Xor
	Ands And
	Refs Ref
}

func ReadCircutFlatten(file string) Circuit {
	strs,err := ioutil.ReadFile(file)
	if err != nil {panic(err)}
	stt := bytes.Split(strs,[]byte("\n"))
	n_max := 10000
	stt = stt[:len(stt)-1]
	
	var cirs Circuit
	inputs := make(Input,len(stt))
	outputs := make(Output,len(stt))
	xors    := make(Xor,len(stt))
	ands    := make(And,len(stt))
	refs    := make(Ref,len(stt))

	for id,s := range stt{
		t := bytes.Split(s,[]byte(" "))
		if len(t)==2{
			opt := bytes.NewBuffer(t[0]).String()
			lef,_ := strconv.Atoi(bytes.NewBuffer(t[1]).String())
			switch opt {
			case "in":
				inputs[lef]=struct{}{}
			case "out":
				outputs[lef]=struct{}{}
			case "ref":
				refs[id+n_max]=Oprand{lef,-1}
				inputs[id]=struct{}{}
			}
		}
		if len(t)==3{
			opt := bytes.NewBuffer(t[0]).String()
			lef,_ := strconv.Atoi(bytes.NewBuffer(t[1]).String())
			rig,_ := strconv.Atoi(bytes.NewBuffer(t[2]).String())
			switch opt {
			case "xor":
				xors[id] = Oprand{lef,rig}
			case "and":
				ands[id+n_max] = Oprand{lef,rig}
				inputs[id]=struct{}{}
			}
		}
	}
	for id,_ := range inputs{
		if _,ok:=outputs[id];ok{delete(inputs,id)}
	}
	cirs = Circuit{inputs,outputs,xors,ands,refs}
	return cirs
}

func ReadCircut(file string) Circuit {
	strs,err := ioutil.ReadFile(file)
	if err != nil {panic(err)}
	stt := bytes.Split(strs,[]byte("\n"))
	stt = stt[:len(stt)-1]
	
	var cirs Circuit
	inputs := make(Input,len(stt))
	outputs := make(Output,len(stt))
	xors    := make(Xor,len(stt))
	ands    := make(And,len(stt))
	refs    := make(Ref,len(stt))

	for id,s := range stt{
		t := bytes.Split(s,[]byte(" "))
		if len(t)==2{
			opt := bytes.NewBuffer(t[0]).String()
			lef,_ := strconv.Atoi(bytes.NewBuffer(t[1]).String())
			switch opt {
			case "in":
				inputs[lef]=struct{}{}
			case "out":
				outputs[lef]=struct{}{}
			case "ref":
				refs[id]=Oprand{lef,-1}
				inputs[id]=struct{}{}
			}
		}
		if len(t)==3{
			opt := bytes.NewBuffer(t[0]).String()
			lef,_ := strconv.Atoi(bytes.NewBuffer(t[1]).String())
			rig,_ := strconv.Atoi(bytes.NewBuffer(t[2]).String())
			switch opt {
			case "xor":
				xors[id] = Oprand{lef,rig}
			case "and":
				ands[id] = Oprand{lef,rig}
				inputs[id]=struct{}{}
			}
		}
	}
	for id,_ := range inputs{
		if _,ok:=outputs[id];ok{delete(inputs,id)}
	}
	cirs = Circuit{inputs,outputs,xors,ands,refs}
	return cirs
}



func main(){

	dir := "./circuits/input_number_3_2/"
	save_dir := "./circuits/Flatten/3in_2/"
	tmpdot := "temp.dot"
	temps, err:= template.ParseFiles(tmpdot)
	filelist,err := ioutil.ReadDir(dir)
	if err != nil { panic(err)}
	for _,file := range filelist{
		filename := file.Name()
		if filename != "result.txt" {
			fmt.Println(dir+filename)
			circ := ReadCircutFlatten(dir+filename)
			fw,err := os.OpenFile(save_dir+filename+".dot", os.O_WRONLY | os.O_TRUNC | os.O_CREATE, 0777)
			if err != nil {panic(err)}
			temps.Execute(fw, circ)
		}
	}
	// s := "c19.txt"
	// circ := ReadCircutFlatten(s)
	// fmt.Println(circ)

	// temps, err:= template.ParseFiles("temp.dot")
	// if err != nil {panic(err)}
	// // temps.Execute(os.Stdout, circ)
	// fw,err := os.OpenFile("c19.dot", os.O_WRONLY | os.O_TRUNC | os.O_CREATE, 0777)
	// // defer close(fw)
	// temps.Execute(fw, circ)
}